package com.jim.director.framework.validation;

import com.jim.director.framework.annotation.*;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 系统注解校验器
 * 校验@SystemFrame、@Status、@Log、@Init等系统注解的合法性
 *
 * @author Jim
 * @date 2024/10/5
 */
public class SystemAnnotationValidator implements AnnotationValidator {

    @Override
    public ValidationResult validate(Class<?> clazz) {
        ValidationResult result = new ValidationResult(clazz.getSimpleName());

        // 只校验带有@Actor或@Configure注解的类
        if (!clazz.isAnnotationPresent(Actor.class) && !clazz.isAnnotationPresent(Configure.class)) {
            return result;
        }

        // 校验字段注解
        validateFieldAnnotations(clazz, result);

        // 校验方法注解
        validateMethodAnnotations(clazz, result);

        // 校验类级别注解
        validateClassAnnotations(clazz, result);

        return result;
    }

    /**
     * 校验字段注解
     */
    private void validateFieldAnnotations(Class<?> clazz, ValidationResult result) {
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            // 校验@SystemFrame注解
            validateSystemFrameField(field, result, clazz);

            // 校验@Status注解
            validateStatusField(field, result, clazz);

            // 校验@LogIgnore注解
            validateLogIgnoreField(field, result, clazz);
        }
    }

    /**
     * 校验@SystemFrame注解
     */
    private void validateSystemFrameField(Field field, ValidationResult result, Class<?> clazz) {
        if (!field.isAnnotationPresent(SystemFrame.class)) {
            return;
        }

        // 1. 校验字段类型
        Class<?> fieldType = field.getType();
        if (!fieldType.equals(int.class) && !fieldType.equals(Integer.class)) {
            result.addError(String.format(
                "类 %s 的字段 %s 标注了 @SystemFrame 注解，但字段类型是 %s。" +
                "@SystemFrame 注解只能标注在 int 或 Integer 类型字段上。",
                clazz.getSimpleName(), field.getName(), fieldType.getSimpleName()
            ));
        }

        // 2. 校验字段修饰符
        if (Modifier.isStatic(field.getModifiers())) {
            result.addError(String.format(
                "类 %s 的字段 %s 标注了 @SystemFrame 注解，但是static字段。" +
                "@SystemFrame 不支持静态字段。",
                clazz.getSimpleName(), field.getName()
            ));
        }

        if (Modifier.isFinal(field.getModifiers())) {
            result.addError(String.format(
                "类 %s 的字段 %s 标注了 @SystemFrame 注解，但是final字段。" +
                "final字段无法被框架修改注入帧数。",
                clazz.getSimpleName(), field.getName()
            ));
        }

        // 3. 给出使用建议
        if (Modifier.isPublic(field.getModifiers())) {
            result.addWarning(String.format(
                "类 %s 的字段 %s 标注了 @SystemFrame 注解，但是public字段。" +
                "建议使用private字段并提供getter方法以保护字段安全。",
                clazz.getSimpleName(), field.getName()
            ));
        }
    }

    /**
     * 校验@Status注解
     */
    private void validateStatusField(Field field, ValidationResult result, Class<?> clazz) {
        if (!field.isAnnotationPresent(Status.class)) {
            return;
        }

        // 1. 校验字段类型
        Class<?> fieldType = field.getType();
        if (!fieldType.equals(String.class)) {
            result.addError(String.format(
                "类 %s 的字段 %s 标注了 @Status 注解，但字段类型是 %s。" +
                "@Status 注解只能标注在 String 类型字段上。",
                clazz.getSimpleName(), field.getName(), fieldType.getSimpleName()
            ));
        }

        // 2. 校验字段修饰符
        if (Modifier.isStatic(field.getModifiers())) {
            result.addError(String.format(
                "类 %s 的字段 %s 标注了 @Status 注解，但是static字段。" +
                "@Status 不支持静态字段。",
                clazz.getSimpleName(), field.getName()
            ));
        }

        if (Modifier.isFinal(field.getModifiers())) {
            result.addWarning(String.format(
                "类 %s 的字段 %s 标注了 @Status 注解，但是final字段。" +
                "final状态字段可能限制状态变更的灵活性。",
                clazz.getSimpleName(), field.getName()
            ));
        }

        // 3. 检查是否有多个@Status字段
        Field[] allFields = clazz.getDeclaredFields();
        int statusFieldCount = 0;
        for (Field f : allFields) {
            if (f.isAnnotationPresent(Status.class)) {
                statusFieldCount++;
            }
        }

        if (statusFieldCount > 1) {
            result.addWarning(String.format(
                "类 %s 中有多个 @Status 字段。" +
                "通常一个Actor只需要一个状态字段。请确认这是预期的设计。",
                clazz.getSimpleName()
            ));
        }
    }

    /**
     * 校验@LogIgnore注解
     */
    private void validateLogIgnoreField(Field field, ValidationResult result, Class<?> clazz) {
        if (!field.isAnnotationPresent(LogIgnore.class)) {
            return;
        }

        // 检查类是否有@Log注解
        if (!clazz.isAnnotationPresent(Log.class)) {
            result.addWarning(String.format(
                "类 %s 的字段 %s 标注了 @LogIgnore 注解，但类没有标注 @Log 注解。" +
                "@LogIgnore 只在启用日志记录时有效。",
                clazz.getSimpleName(), field.getName()
            ));
        }

        // 检查字段是否为常量
        if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) {
            result.addInfo(String.format(
                "类 %s 的字段 %s 是常量，通常不需要 @LogIgnore 注解。" +
                "框架通常会自动忽略常量字段。",
                clazz.getSimpleName(), field.getName()
            ));
        }
    }

    /**
     * 校验方法注解
     */
    private void validateMethodAnnotations(Class<?> clazz, ValidationResult result) {
        Method[] methods = clazz.getDeclaredMethods();
        int initMethodCount = 0;

        for (Method method : methods) {
            if (method.isAnnotationPresent(Init.class)) {
                initMethodCount++;
                validateInitMethod(method, result, clazz);
            }
        }

        // 检查@Init方法数量
        if (initMethodCount > 1) {
            result.addWarning(String.format(
                "类 %s 中有多个 @Init 方法。" +
                "虽然支持多个初始化方法，但通常一个Actor只需要一个初始化方法。",
                clazz.getSimpleName()
            ));
        }
    }

    /**
     * 校验@Init方法
     */
    private void validateInitMethod(Method method, ValidationResult result, Class<?> clazz) {
        // 1. 校验方法签名
        if (!method.getReturnType().equals(void.class)) {
            result.addError(String.format(
                "类 %s 的方法 %s 标注了 @Init 注解，但返回类型不是void。" +
                "初始化方法必须返回void类型。",
                clazz.getSimpleName(), method.getName()
            ));
        }

        if (method.getParameterCount() > 0) {
            result.addError(String.format(
                "类 %s 的方法 %s 标注了 @Init 注解，但包含参数。" +
                "初始化方法不能有参数。",
                clazz.getSimpleName(), method.getName()
            ));
        }

        // 2. 校验方法修饰符
        if (Modifier.isPrivate(method.getModifiers())) {
            result.addError(String.format(
                "类 %s 的方法 %s 标注了 @Init 注解，但是private方法。" +
                "框架无法访问private方法。请将方法修改为public。",
                clazz.getSimpleName(), method.getName()
            ));
        }

        if (Modifier.isStatic(method.getModifiers())) {
            result.addError(String.format(
                "类 %s 的方法 %s 标注了 @Init 注解，但是static方法。" +
                "初始化方法不能是静态方法。",
                clazz.getSimpleName(), method.getName()
            ));
        }

        if (Modifier.isAbstract(method.getModifiers())) {
            result.addError(String.format(
                "类 %s 的方法 %s 标注了 @Init 注解，但是abstract方法。" +
                "初始化方法必须有具体实现。",
                clazz.getSimpleName(), method.getName()
            ));
        }
    }

    /**
     * 校验类级别注解
     */
    private void validateClassAnnotations(Class<?> clazz, ValidationResult result) {
        // 校验@Log注解
        if (clazz.isAnnotationPresent(Log.class)) {
            validateLogAnnotation(clazz, result);
        }
    }

    /**
     * 校验@Log注解
     */
    private void validateLogAnnotation(Class<?> clazz, ValidationResult result) {
        Log log = clazz.getAnnotation(Log.class);

        // 1. 校验period参数
        int period = log.period();
        if (period <= 0) {
            result.addError(String.format(
                "类 %s 的 @Log 注解: period参数值 %d 必须大于0。",
                clazz.getSimpleName(), period
            ));
        }

        if (period == 1) {
            result.addWarning(String.format(
                "类 %s 的 @Log 注解: period参数值为1，将在每帧都记录日志。" +
                "这可能产生大量日志输出，请确认这是预期行为。",
                clazz.getSimpleName()
            ));
        }

        // 2. 校验target参数
        String target = log.target();
        if (target == null || target.trim().isEmpty()) {
            result.addError(String.format(
                "类 %s 的 @Log 注解: target参数不能为空。",
                clazz.getSimpleName()
            ));
        } else if (!target.equals("System.out") && !target.endsWith(".txt") && !target.endsWith(".log")) {
            result.addWarning(String.format(
                "类 %s 的 @Log 注解: target参数值 '%s' 不是标准的日志目标。" +
                "建议使用 'System.out' 或以 '.txt'/'.log' 结尾的文件名。",
                clazz.getSimpleName(), target
            ));
        }
    }

    @Override
    public Class<?>[] getSupportedAnnotations() {
        return new Class<?>[]{SystemFrame.class, Status.class, LogIgnore.class, Init.class, Log.class};
    }

    @Override
    public String getValidatorName() {
        return "SystemAnnotationValidator";
    }
}

